/*
Copyright (c) 2025 HeZongLun
NahidaProject is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

#pragma once

#ifdef _WIN32
#define ExportSymbol  __declspec(dllexport)
#else
#define ExportSymbol
#endif

#include <string>
#include <stdexcept>

#ifdef _WIN32
#include <Windows.h>
typedef ExportSymbol HMODULE LibraryHandle;
#else
#include <dlfcn.h>
typedef void* LibraryHandle;
#endif

namespace NahidaProject {
	class ExportSymbol DynamicLibraryLoader {
	private:
		LibraryHandle handle;
		std::string libraryPath;
		bool isLoaded;

	public:
		explicit DynamicLibraryLoader(const std::string& libraryPath = "");
		~DynamicLibraryLoader();

		DynamicLibraryLoader(const DynamicLibraryLoader&) = delete;
		DynamicLibraryLoader& operator=(const DynamicLibraryLoader&) = delete;
		DynamicLibraryLoader(DynamicLibraryLoader&& other) noexcept;
		DynamicLibraryLoader& operator=(DynamicLibraryLoader&& other) noexcept;

		bool Load(const std::string& libraryPath);
		bool Unload();

		template<typename T> T GetFunction(const std::string& functionName) {
			if (!isLoaded) {
				throw std::runtime_error("Library not loaded");
			}

			void* symbol = GetSymbolImplement(functionName);
			if (symbol == nullptr) {
				throw std::runtime_error("Function '" + functionName + "' not found: " + GetLastError());
			}

			return reinterpret_cast<T>(symbol);
		}

		bool IsLoaded() const { return isLoaded; }
		std::string GetLastError() const;
		const std::string& GetLibraryPath() const { return libraryPath; }

	private:
		bool LoadImplement(const std::string& path);
		bool UnloadImplement();
		void* GetSymbolImplement(const std::string& symbolName);
	};
}
